﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Practices.Composite.Events;
using Might.Infrastructure.DOM;
using Might.Infrastructure.Events;
using Might.Infrastructure.Services;

namespace Might.Infrastructure.Annotations.Graphics {
    public class GraphicsAnnotation : DomNode, IGraphicsAnnotation {

        public GraphicsAnnotation(ObservableCollection<IGraphicsItem> children, List<IGraphicsAnnotation> list ) {
            this.children = children;
            layers = list;
            eventAggregator = CoreServices.EventAggregator;
            Graphics.CollectionChanged += IconChanged;
        }
        
        public List<IGraphicsAnnotation> InheritedGraphics {
            get { return layers; }
        }
        private readonly List<IGraphicsAnnotation> layers;
        
        public ObservableCollection<IGraphicsItem> Graphics {
            get { return children; }
        }
        private readonly ObservableCollection<IGraphicsItem> children;

        public IGraphicsItem this[int index] {
            get {
                return (index >= 0 && index < Graphics.Count) ? children[index] : null;
            }
        }

        private readonly IEventAggregator eventAggregator;

        void IconChanged(object sender, EventArgs e) {
            OnChanged(sender, "Children");
        }
      
        public void AddPrimitive(IGraphicsItem primitive) {
            if (primitive != null) {
                Graphics.Add(primitive);
            }
        }

        public void RemovePrimitive(IGraphicsItem primitive) {
            if (primitive != null) {
                if (Graphics.Contains(primitive)) {
                    Graphics.Remove(primitive);
                } else {
                    throw new ArgumentException("Primitive is not in this layer.");
                }
            }
        }

        #region Z-order manipulation
        // TODO add type check
        public void BringForward(List<IGraphicsItem> list) {
            StartUpdate();
            List<KeyValuePair<int, IGraphicsItem>> dict = GetDict(list);

            for (int i = dict.Count - 1; i >= 0; i--) {
                int pos = Math.Min(dict[i].Key + 1, Graphics.Count);
                Graphics.Insert(pos, dict[i].Value);
            }
            FinishUpdate();
        }

        public void SendBackward(List<IGraphicsItem> list) {
            StartUpdate();
            List<KeyValuePair<int, IGraphicsItem>> dict = GetDict(list);

            int minpos = 0;
            for (int i = dict.Count - 1; i >= 0; i--) {
                int pos = Math.Max(dict[i].Key - 1, minpos);
                if (pos == minpos) {
                    minpos++;
                }
                Graphics.Insert(pos, dict[i].Value);
            }
            FinishUpdate();
        }

        public void BringToFront(List<IGraphicsItem> list) {
            StartUpdate();
            List<KeyValuePair<int, IGraphicsItem>> dict = GetDict(list);

            for (int i = dict.Count - 1; i >= 0; i--) {
                Graphics.Insert(Graphics.Count, dict[i].Value);
            }
            FinishUpdate();
        }

        public void SendToBack(List<IGraphicsItem> list) {
            StartUpdate();
            List<KeyValuePair<int, IGraphicsItem>> dict = GetDict(list);

            for (int i = 0; i < dict.Count; i++) {
                Graphics.Insert(0, dict[i].Value);
            }
            FinishUpdate();
        }

        #endregion Z-order manipulation

        public void MultipleSelect(List<IGraphicsItem> primitives) {
            // TODO change this, Modelica library should be unaware of composite framework
            eventAggregator.GetEvent<GraphicsPrimitiveMultipleSelectedEvent>().Publish(primitives);
        }

        public void MultipleChanged() {
            eventAggregator.GetEvent<GraphicsPrimitiveMultipleChangedEvent>().Publish(null);
        }

        // Internals

        /// <summary>
        /// Extracts selected list of Graphics from "Graphics" list into dictionary
        /// with info about the previous index of each primitive. 
        /// Used in z-order manipulation functions.
        /// </summary>
        /// <param name="list">List of selected Graphics.</param>
        /// <returns>Created indexed list of Graphics.</returns>
        private List<KeyValuePair<int, IGraphicsItem>> GetDict(ICollection<IGraphicsItem> list) {
            List<KeyValuePair<int, IGraphicsItem>> dict = new List<KeyValuePair<int, IGraphicsItem>>();
            for (int i = Graphics.Count - 1; i >= 0; i--) {
                if (list.Contains(Graphics[i])) {
                    dict.Add(new KeyValuePair<int, IGraphicsItem>(i, Graphics[i]));
                    Graphics.RemoveAt(i);
                }
            }
            //throw new ArgumentException("Primitive is not in this layer.");
            return dict;
        }
    }
}